1use crate::scripts::base::*;
2use crate::types::*;
3use crate::utils::encoding::*;
4use crate::utils::escape::*;
5use anyhow::Result;
6use std::collections::HashSet;
7use std::ops::{Deref, DerefMut};
8use std::sync::Arc;
9use unicode_segmentation::UnicodeSegmentation;
10
11#[derive(Debug, Clone)]
12pub struct TxtBuilder {}
14
15impl TxtBuilder {
16 pub const fn new() -> Self {
18 Self {}
19 }
20}
21
22impl ScriptBuilder for TxtBuilder {
23 fn default_encoding(&self) -> Encoding {
24 Encoding::Utf8
25 }
26
27 fn build_script(
28 &self,
29 buf: Vec<u8>,
30 _filename: &str,
31 encoding: Encoding,
32 _archive_encoding: Encoding,
33 config: &ExtraConfig,
34 _archive: Option<&Box<dyn Script>>,
35 ) -> Result<Box<dyn Script>> {
36 Ok(Box::new(TxtScript::new(buf, encoding, config)?))
37 }
38
39 fn extensions(&self) -> &'static [&'static str] {
40 &["txt"]
41 }
42
43 fn script_type(&self) -> &'static ScriptType {
44 &ScriptType::ArtemisPanmimisoftTxt
45 }
46}
47
48pub trait Node {
50 fn serialize(&self) -> String;
52}
53
54#[derive(Debug, Clone, PartialEq)]
55pub struct CommentNode(pub String);
57
58impl Node for CommentNode {
59 fn serialize(&self) -> String {
60 format!("//{}", self.0)
61 }
62}
63
64#[derive(Clone, Debug, PartialEq)]
65pub struct EmptyLineNode;
67
68impl Node for EmptyLineNode {
69 fn serialize(&self) -> String {
70 String::new()
71 }
72}
73
74#[derive(Debug, Clone, PartialEq)]
75pub struct LabelNode(pub String);
77
78impl Node for LabelNode {
79 fn serialize(&self) -> String {
80 format!("*{}", self.0)
81 }
82}
83
84#[derive(Debug, Clone, PartialEq)]
85pub struct TagNode {
87 pub name: String,
89 pub attributes: Vec<(String, Option<String>)>,
91}
92
93impl Node for TagNode {
94 fn serialize(&self) -> String {
95 let attributes = self
96 .attributes
97 .iter()
98 .map(|(key, value)| {
99 if let Some(val) = value {
100 format!("{}=\"{}\"", key, val)
101 } else {
102 key.clone()
103 }
104 })
105 .collect::<Vec<_>>()
106 .join(" ");
107 if attributes.is_empty() {
108 format!("[{}]", self.name)
109 } else {
110 format!("[{} {}]", self.name, attributes)
111 }
112 }
113}
114
115impl TagNode {
116 fn ser_attributes_xml(&self) -> String {
117 let mut parts = Vec::new();
118 for (key, value) in self.attributes.iter() {
119 match value {
120 None => {
121 parts.push(key.clone());
122 }
123 Some(val) => {
124 parts.push(format!("{}=\"{}\"", key, escape_xml_attr_value(val)));
125 }
126 }
127 }
128 parts.join(" ")
129 }
130
131 pub fn get_attr(&self, attr: &str) -> Option<&str> {
133 self.attributes
134 .iter()
135 .find(|(key, _)| key == attr)
136 .and_then(|(_, value)| value.as_deref())
137 }
138
139 pub fn is_blocked_name(&self, set: &HashSet<String>) -> bool {
141 self.name.is_ascii() || set.contains(&self.name)
142 }
143
144 pub fn has_attr(&self, attr: &str) -> bool {
146 self.attributes.iter().any(|(key, _)| key == attr)
147 }
148
149 pub fn set_attr(&mut self, attr: &str, value: Option<String>) {
151 if let Some(pos) = self.attributes.iter().position(|(key, _)| key == attr) {
152 self.attributes[pos].1 = value;
153 } else {
154 self.attributes.push((attr.to_string(), value));
155 }
156 }
157
158 pub fn to_xml(&self) -> String {
160 let attributes = self.ser_attributes_xml();
161 if attributes.is_empty() {
162 format!("<{}>", self.name)
163 } else {
164 format!("<{} {}>", self.name, attributes)
165 }
166 }
167}
168
169#[derive(Debug, Clone, PartialEq)]
170pub struct TextNode(pub String);
172
173#[derive(Debug, Clone, PartialEq)]
174pub enum TxtLineNode {
176 Comment(CommentNode),
177 Tag(TagNode),
178 Text(TextNode),
179}
180
181impl TxtLineNode {
182 pub fn is_tag(&self, tag: &str) -> bool {
186 matches!(self, TxtLineNode::Tag(node) if node.name == tag)
187 }
188
189 pub fn is_tag_blocked_name(&self, set: &HashSet<String>) -> bool {
191 if let TxtLineNode::Tag(node) = self {
192 node.is_blocked_name(set)
193 } else {
194 false
195 }
196 }
197
198 pub fn tag_attr_keys<'a>(&'a self) -> Box<dyn Iterator<Item = &'a str> + 'a> {
200 if let TxtLineNode::Tag(node) = self {
201 Box::new(node.attributes.iter().map(|(key, _)| key.as_str()))
202 } else {
203 Box::new(std::iter::empty())
204 }
205 }
206
207 pub fn tag_get_attr<'a>(&'a self, attr: &str) -> Option<&'a str> {
208 if let TxtLineNode::Tag(node) = self {
209 node.get_attr(attr)
210 } else {
211 None
212 }
213 }
214
215 pub fn tag_has_attr(&self, attr: &str) -> bool {
217 if let TxtLineNode::Tag(node) = self {
218 node.attributes.iter().any(|(key, _)| key == attr)
219 } else {
220 false
221 }
222 }
223
224 pub fn tag_set_attr(&mut self, attr: &str, value: Option<String>) {
225 if let TxtLineNode::Tag(node) = self {
226 node.set_attr(attr, value);
227 }
228 }
229
230 pub fn to_xml(&self) -> String {
232 match self {
233 TxtLineNode::Comment(_) => String::new(), TxtLineNode::Tag(n) => {
235 if (n.name == "rt2" || n.name == "ret2") && n.attributes.is_empty() {
236 "\n".to_string()
237 } else {
238 n.to_xml()
239 }
240 }
241 TxtLineNode::Text(n) => escape_xml_text_value(&n.0),
242 }
243 }
244}
245
246impl Node for TxtLineNode {
247 fn serialize(&self) -> String {
248 match self {
249 TxtLineNode::Comment(node) => node.serialize(),
250 TxtLineNode::Tag(node) => node.serialize(),
251 TxtLineNode::Text(node) => node.0.clone(),
252 }
253 }
254}
255
256#[derive(Debug, Clone, PartialEq)]
257pub struct TxtLine(pub Vec<TxtLineNode>);
259
260impl Deref for TxtLine {
261 type Target = Vec<TxtLineNode>;
262
263 fn deref(&self) -> &Self::Target {
264 &self.0
265 }
266}
267
268impl DerefMut for TxtLine {
269 fn deref_mut(&mut self) -> &mut Self::Target {
270 &mut self.0
271 }
272}
273
274impl Node for TxtLine {
275 fn serialize(&self) -> String {
276 self.0
277 .iter()
278 .map(|node| node.serialize())
279 .collect::<Vec<_>>()
280 .join("")
281 }
282}
283
284impl TxtLine {
285 pub fn to_xml(&self) -> String {
287 self.0
288 .iter()
289 .map(|node| node.to_xml())
290 .collect::<Vec<_>>()
291 .join("")
292 }
293}
294
295#[derive(Debug, Clone, PartialEq)]
296pub enum ParsedLine {
298 Empty(EmptyLineNode),
300 Comment(CommentNode),
302 Label(LabelNode),
304 Line(TxtLine),
306}
307
308impl Node for ParsedLine {
309 fn serialize(&self) -> String {
310 match self {
311 ParsedLine::Empty(node) => node.serialize(),
312 ParsedLine::Comment(node) => node.serialize(),
313 ParsedLine::Label(node) => node.serialize(),
314 ParsedLine::Line(line) => line.serialize(),
315 }
316 }
317}
318
319impl ParsedLine {
320 pub fn len(&self) -> usize {
322 match self {
323 ParsedLine::Empty(_) => 0,
324 ParsedLine::Comment(_) => 0,
325 ParsedLine::Label(_) => 0,
326 ParsedLine::Line(line) => line.len(),
327 }
328 }
329
330 pub fn push(&mut self, node: TxtLineNode) {
332 if let ParsedLine::Line(line) = self {
333 line.push(node);
334 } else {
335 }
337 }
338
339 pub fn insert(&mut self, index: usize, node: TxtLineNode) {
341 if let ParsedLine::Line(line) = self {
342 line.insert(index, node);
343 } else {
344 }
346 }
347
348 pub fn remove(&mut self, index: usize) -> Option<TxtLineNode> {
350 if let ParsedLine::Line(line) = self {
351 if index < line.len() {
352 Some(line.remove(index))
353 } else {
354 None
355 }
356 } else {
357 None
359 }
360 }
361}
362
363#[derive(Debug, Clone)]
364pub struct ParsedScript(pub Vec<ParsedLine>);
366
367impl Deref for ParsedScript {
368 type Target = Vec<ParsedLine>;
369
370 fn deref(&self) -> &Self::Target {
371 &self.0
372 }
373}
374
375impl DerefMut for ParsedScript {
376 fn deref_mut(&mut self) -> &mut Self::Target {
377 &mut self.0
378 }
379}
380
381impl Node for ParsedScript {
382 fn serialize(&self) -> String {
383 self.0
384 .iter()
385 .map(|line| line.serialize())
386 .collect::<Vec<_>>()
387 .join("\n")
388 }
389}
390
391pub struct Parser {
393 lines: Vec<String>,
394}
395
396impl Parser {
397 pub fn new<S: AsRef<str> + ?Sized>(script: &S) -> Self {
398 let lines = script.as_ref().lines().map(|s| s.to_string()).collect();
399 Self { lines }
400 }
401
402 pub fn parse(&self, preserve_empty_lines: bool) -> Result<ParsedScript> {
403 let mut parsed_script = Vec::new();
404 let mut i = 0;
405 let line_count = self.lines.len();
406 while i < line_count {
407 let line = self.lines[i].trim();
408 i += 1;
409 if line.is_empty() {
410 if preserve_empty_lines {
411 parsed_script.push(ParsedLine::Empty(EmptyLineNode));
412 }
413 continue;
414 }
415 if line.starts_with("//") {
416 parsed_script.push(ParsedLine::Comment(CommentNode(line[2..].to_string())));
417 continue;
418 }
419 if line.starts_with("*") {
420 let label = line[1..].trim().to_string();
421 parsed_script.push(ParsedLine::Label(LabelNode(label)));
422 continue;
423 }
424 let mut temp = String::new();
425 let mut nodes = Vec::new();
426 let mut line_graphs = line.graphemes(true).collect::<Vec<_>>();
427 let mut line_pos = 0;
428 let mut is_comment = false;
429 while line_pos < line_graphs.len() {
430 let graph = line_graphs[line_pos];
431 line_pos += 1;
432 temp.push_str(graph);
433 if is_comment {
434 continue;
435 }
436 if !is_comment && temp.ends_with("//") && temp.len() > 2 {
437 nodes.push(TxtLineNode::Text(TextNode(
438 temp[..temp.len() - 2].to_string(),
439 )));
440 temp.clear();
441 is_comment = true;
442 continue;
443 }
444 if graph == "[" {
445 if !temp.trim_end_matches("[").is_empty() {
446 nodes.push(TxtLineNode::Text(TextNode(
447 temp.trim_end_matches("[").to_string(),
448 )));
449 }
450 while !line_graphs[line_pos..].contains(&"]") {
452 if i < line_count {
453 let nline = self.lines[i].trim();
454 i += 1;
455 line_graphs.push("\n");
457 line_graphs.extend(nline.graphemes(true));
458 } else {
459 break;
460 }
461 }
462 let (tag, nextpos) = TagParser {
463 graphs: &line_graphs,
464 pos: line_pos,
465 }
466 .parse()?;
467 line_pos = nextpos;
468 nodes.push(TxtLineNode::Tag(tag));
469 temp.clear();
470 continue;
471 }
472 }
473 if is_comment {
474 nodes.push(TxtLineNode::Comment(CommentNode(temp)));
475 } else {
476 if !temp.is_empty() {
477 nodes.push(TxtLineNode::Text(TextNode(temp)));
478 }
479 }
480 parsed_script.push(ParsedLine::Line(TxtLine(nodes)));
481 }
482 Ok(ParsedScript(parsed_script))
483 }
484}
485
486struct TagParser<'a> {
487 graphs: &'a [&'a str],
488 pos: usize,
489}
490
491impl<'a> TagParser<'a> {
492 fn peek(&self) -> Option<&'a str> {
493 self.graphs.get(self.pos).cloned()
494 }
495
496 fn eat(&mut self) {
497 if self.pos < self.graphs.len() {
498 self.pos += 1;
499 }
500 }
501
502 fn next(&mut self) -> Option<&'a str> {
503 if self.pos < self.graphs.len() {
504 let graph = self.graphs[self.pos];
505 self.pos += 1;
506 Some(graph)
507 } else {
508 None
509 }
510 }
511
512 fn is_indent(&self, indent: &str) -> bool {
513 let mut pos = self.pos;
514 for ident in indent.graphemes(true) {
515 if pos >= self.graphs.len() || self.graphs[pos] != ident {
516 return false;
517 }
518 pos += 1;
519 }
520 true
521 }
522
523 fn eat_all_equal(&mut self) {
524 while let Some(graph) = self.peek() {
525 if graph == "=" {
526 self.eat();
527 } else {
528 break;
529 }
530 }
531 }
532
533 fn parse(&mut self) -> Result<(TagNode, usize)> {
534 let name = self.parse_tag()?;
535 self.erase_whitespace();
536 let mut attributes = Vec::new();
537 loop {
538 let graph = match self.peek() {
539 Some(g) => g,
540 None => {
541 return Err(anyhow::anyhow!(
542 "Unexpected end of tag parsing: {}",
543 self.graphs.join("")
544 ));
545 }
546 };
547 if graph == "]" {
548 self.eat();
549 break;
550 }
551 if graph == " " || graph == "\t" {
552 self.eat();
553 continue;
554 }
555 if graph == "=" {
556 return Err(anyhow::anyhow!("Unexpected '=' without attribute name"));
557 }
558 let attr_name = self.parse_attr_name()?;
559 self.erase_whitespace();
560 let graph = match self.peek() {
561 Some(g) => g,
562 None => {
563 return Err(anyhow::anyhow!(
564 "Unexpected end of tag parsing: {}",
565 self.graphs.join("")
566 ));
567 }
568 };
569 if graph == "]" {
570 self.eat();
571 attributes.push((attr_name, None));
572 break;
573 }
574 if graph == "=" {
575 self.eat_all_equal();
579 self.erase_whitespace();
580 let value = self.parse_attr_value()?;
581 attributes.push((attr_name, Some(value)));
582 self.erase_whitespace();
583 } else {
584 attributes.push((attr_name, None));
585 self.erase_whitespace();
586 continue;
587 }
588 }
589 return Ok((TagNode { name, attributes }, self.pos));
590 }
591
592 fn erase_whitespace(&mut self) {
593 while let Some(graph) = self.peek() {
594 if graph == " " || graph == "\t" {
595 self.eat();
596 } else {
597 break;
598 }
599 }
600 }
601
602 fn parse_attr_name(&mut self) -> Result<String> {
603 let mut attr_name = String::new();
604 while let Some(graph) = self.peek() {
605 if graph == "=" || graph == " " || graph == "\t" || graph == "]" {
606 break;
607 }
608 attr_name.push_str(graph);
609 self.eat();
610 }
611 if attr_name.is_empty() {
612 return Err(anyhow::anyhow!("Empty attribute name found"));
613 }
614 Ok(attr_name)
615 }
616
617 fn parse_attr_value(&mut self) -> Result<String> {
618 let mut value = String::new();
619 if !self.is_indent("\"") {
620 return Err(anyhow::anyhow!(
621 "Expected attribute value to start with a quote: {}",
622 self.graphs.join("")
623 ));
624 }
625 self.eat(); while let Some(graph) = self.next() {
627 if graph == "\"" {
628 break; }
630 value.push_str(graph);
631 }
632 Ok(value)
633 }
634
635 fn parse_tag(&mut self) -> Result<String> {
636 let mut tag = String::new();
637 while let Some(graph) = self.peek() {
638 if graph == " " || graph == "\t" || graph == "]" {
639 break;
640 }
641 tag.push_str(graph);
642 self.eat();
643 }
644 if tag.is_empty() {
645 return Err(anyhow::anyhow!("Empty tag found"));
646 }
647 Ok(tag)
648 }
649}
650
651struct XMLTextParser<'a> {
652 str: &'a str,
653 lang: &'a str,
654 pos: usize,
655}
656
657impl<'a> XMLTextParser<'a> {
658 pub fn new(text: &'a str, lang: &'a str) -> Self {
659 Self {
660 str: text,
661 lang,
662 pos: 0,
663 }
664 }
665
666 fn parse_tag(&mut self) -> Result<TagNode> {
667 let mut name = String::new();
668 let mut attributes = Vec::new();
669 let mut is_name = true;
670 let mut is_key = false;
671 let mut is_value = false;
672 let mut is_in_quote = false;
673 let mut key = String::new();
674 let mut value = String::new();
675 while let Some(c) = self.next() {
676 match c {
677 '>' => {
678 if !name.is_empty() {
679 return Ok(TagNode { name, attributes });
680 } else {
681 return Err(anyhow::anyhow!("Empty tag name"));
682 }
683 }
684 ' ' | '\t' => {
685 if is_name {
686 is_name = false;
687 is_key = true;
688 } else if is_key {
689 if !key.is_empty() {
690 attributes.push((key.clone(), None));
691 key.clear();
692 }
693 } else if is_value {
694 if is_in_quote {
695 value.push(c);
696 } else {
697 if !value.is_empty() {
698 attributes.push((key.clone(), Some(unescape_xml(&value))));
699 key.clear();
700 value.clear();
701 }
702 is_key = true;
703 is_value = false;
704 }
705 }
706 }
707 '"' => {
708 if is_in_quote {
709 is_in_quote = false;
710 if !value.is_empty() {
711 attributes.push((key.clone(), Some(unescape_xml(&value))));
712 key.clear();
713 value.clear();
714 }
715 is_key = true;
716 } else {
717 is_in_quote = true;
718 }
719 }
720 '=' => {
721 if is_key {
722 is_key = false;
723 is_value = true;
724 }
725 }
726 _ => {
727 if is_name {
728 name.push(c);
729 } else if is_key {
730 key.push(c);
731 } else if is_value {
732 value.push(c);
733 } else {
734 return Err(anyhow::anyhow!("Unexpected character in tag: {}", c));
735 }
736 }
737 }
738 }
739 Err(anyhow::anyhow!("Unexpected end of input while parsing tag"))
740 }
741
742 pub fn parse(mut self) -> Result<Vec<ParsedLine>> {
743 let mut lines = Vec::new();
744 let mut current_line = Vec::new();
745 let mut text = String::new();
746 current_line.push(TxtLineNode::Tag(TagNode {
747 name: "lang".to_string(),
748 attributes: vec![(self.lang.to_string(), None)],
749 }));
750 while let Some(c) = self.next() {
751 match c {
752 '<' => {
753 if !text.is_empty() {
754 current_line.push(TxtLineNode::Text(TextNode(unescape_xml(&text))));
755 text.clear();
756 }
757 let tag = self.parse_tag()?;
758 let is_r = tag.name == "rt2" || tag.name == "ret2";
759 current_line.push(TxtLineNode::Tag(tag));
760 if is_r {
761 lines.push(ParsedLine::Line(TxtLine(current_line)));
762 current_line = Vec::new();
763 }
764 }
765 '\n' => {
766 if !text.is_empty() {
767 current_line.push(TxtLineNode::Text(TextNode(unescape_xml(&text))));
768 text.clear();
769 }
770 current_line.push(TxtLineNode::Tag(TagNode {
771 name: "rt2".to_string(),
772 attributes: Vec::new(),
773 }));
774 lines.push(ParsedLine::Line(TxtLine(current_line)));
775 current_line = Vec::new();
776 }
777 _ => text.push(c),
778 }
779 }
780 if !text.is_empty() {
781 current_line.push(TxtLineNode::Text(TextNode(unescape_xml(&text))));
782 }
783 current_line.push(TxtLineNode::Tag(TagNode {
784 name: "/lang".to_string(),
785 attributes: Vec::new(),
786 }));
787 lines.push(ParsedLine::Line(TxtLine(current_line)));
788 Ok(lines)
789 }
790
791 fn next(&mut self) -> Option<char> {
792 if self.pos < self.str.len() {
793 let c = self.str[self.pos..].chars().next()?;
794 self.pos += c.len_utf8();
795 Some(c)
796 } else {
797 None
798 }
799 }
800}
801
802#[derive(Debug)]
803pub struct TxtScript {
804 tree: ParsedScript,
805 blacklist_names: Arc<HashSet<String>>,
806 lang: Option<String>,
807}
808
809impl TxtScript {
810 pub fn new(buf: Vec<u8>, encoding: Encoding, config: &ExtraConfig) -> Result<Self> {
812 let script = decode_to_string(encoding, &buf, true)?;
813 let parser = Parser::new(&script);
814 let tree = parser.parse(true)?;
815 Ok(Self {
816 tree,
817 blacklist_names: config.artemis_panmimisoft_txt_blacklist_names.clone(),
818 lang: config.artemis_panmimisoft_txt_lang.clone(),
819 })
820 }
821}
822
823impl Script for TxtScript {
824 fn default_output_script_type(&self) -> OutputScriptType {
825 OutputScriptType::Json
826 }
827
828 fn default_format_type(&self) -> FormatOptions {
829 FormatOptions::None
830 }
831
832 fn extract_messages(&self) -> Result<Vec<Message>> {
833 let mut messages = Vec::new();
834 let mut i = 0;
835 let len = self.tree.len();
836 let mut last_tag_block: Option<TagNode> = None;
837 let mut lang = self.lang.as_ref().map(|s| s.as_str());
838 let mut message = TxtLine(Vec::new());
839 let mut in_lang_block = false;
840 let mut droped_lang_block = false;
841 let mut is_selectblk = false;
842 while i < len {
843 let line = &self.tree[i];
844 if let ParsedLine::Line(line) = line {
845 for node in line.iter() {
846 if node.is_tag("lang") {
847 let lan = match lang {
848 Some(l) => l,
849 None => {
850 for key in node.tag_attr_keys() {
851 lang = Some(key);
852 break;
853 }
854 match lang {
855 Some(l) => l,
856 None => {
857 return Err(anyhow::anyhow!(
858 "No language found in lang tag"
859 ));
860 }
861 }
862 }
863 };
864 if node.tag_has_attr(lan) {
865 in_lang_block = true;
866 } else {
867 droped_lang_block = true;
868 }
869 } else if node.is_tag("/lang") {
870 in_lang_block = false;
871 droped_lang_block = false;
872 } else if node.is_tag("printlang") {
873 let mes = message.to_xml();
874 message.clear();
875 if !mes.is_empty() {
876 let name = match &last_tag_block {
877 Some(block) => Some(if let Some(name) = block.get_attr("name") {
878 name.to_string()
879 } else {
880 block.name.clone()
881 }),
882 _ => None,
883 };
884 messages.push(Message { name, message: mes });
885 }
886 last_tag_block = None;
887 } else if node.is_tag("selectbtn_init") {
888 is_selectblk = true;
889 } else if node.is_tag("selectbtn") {
890 let mut lan = match lang {
891 Some(l) => l,
892 None => {
893 for key in node.tag_attr_keys() {
894 if key == "label" || key == "call" {
895 continue;
896 }
897 lang = Some(key);
898 break;
899 }
900 match lang {
901 Some(l) => l,
902 None => {
903 return Err(anyhow::anyhow!(
904 "No language found in selectbtn tag"
905 ));
906 }
907 }
908 }
909 };
910 if !node.tag_has_attr(lan) {
911 for key in node.tag_attr_keys() {
912 if key == "label" || key == "call" {
913 continue;
914 }
915 lan = key;
916 break;
917 }
918 }
919 if let Some(t) = node.tag_get_attr(lan) {
920 messages.push(Message {
921 name: None,
922 message: t.to_string(),
923 });
924 }
925 } else if node.is_tag("/selectbtn") {
926 is_selectblk = false;
927 } else if in_lang_block {
928 message.push(node.clone());
929 } else if droped_lang_block {
930 } else if is_selectblk {
932 } else if let TxtLineNode::Tag(tag) = node {
934 if !tag.is_blocked_name(&self.blacklist_names) {
935 last_tag_block = Some(tag.clone());
936 }
937 }
938 }
939 }
940 i += 1;
941 }
942 Ok(messages)
943 }
944
945 fn import_messages<'a>(
946 &'a self,
947 messages: Vec<Message>,
948 mut file: Box<dyn WriteSeek + 'a>,
949 _filename: &str,
950 encoding: Encoding,
951 replacement: Option<&'a ReplacementTable>,
952 ) -> Result<()> {
953 let mut output = self.tree.clone();
954 let mut current_line = 0;
955 let mut last_tag_block_loc = None;
956 let mut lang = self.lang.clone();
957 let mut mes = messages.iter();
958 let mut mess = mes.next();
959 let mut lang_block_index = None;
960 let mut lang_end_block_index = None;
961 let mut in_lang_block = false;
962 let mut droped_lang_block = false;
963 let mut is_selectblk = false;
964 while current_line < output.len() {
965 let line = output[current_line].clone();
966 if let ParsedLine::Line(line) = &line {
967 for (i, node) in line.iter().enumerate() {
968 if node.is_tag("lang") {
969 let lan = match lang.as_ref() {
970 Some(l) => l.as_str(),
971 None => {
972 for key in node.tag_attr_keys() {
973 lang = Some(key.to_string());
974 break;
975 }
976 match lang.as_ref() {
977 Some(l) => l.as_str(),
978 None => {
979 return Err(anyhow::anyhow!(
980 "No language found in lang tag"
981 ));
982 }
983 }
984 }
985 };
986 if node.tag_has_attr(lan) {
987 in_lang_block = true;
988 lang_block_index = Some((current_line, i));
989 } else {
990 droped_lang_block = true;
991 }
992 } else if node.is_tag("/lang") {
993 if in_lang_block {
994 lang_end_block_index = Some((current_line, i));
995 }
996 in_lang_block = false;
997 droped_lang_block = false;
998 } else if node.is_tag("printlang") {
999 let lan = lang
1000 .as_ref()
1001 .map(|s| s.as_str())
1002 .ok_or(anyhow::anyhow!("No language specified."))?;
1003 let m = match mess {
1004 Some(m) => m,
1005 None => {
1006 return Err(anyhow::anyhow!("Not enough messages."));
1007 }
1008 };
1009 if let Some(name) = &m.name {
1010 let block_index: (usize, usize) = match last_tag_block_loc.take() {
1011 Some(data) => data,
1012 None => {
1013 return Err(anyhow::anyhow!(
1014 "No name tag block found before printlang.",
1015 ));
1016 }
1017 };
1018 let mut name = name.clone();
1019 if let Some(repl) = replacement {
1020 for (k, v) in &repl.map {
1021 name = name.replace(k, v);
1022 }
1023 }
1024 let mblock = &mut output[block_index.0];
1025 if let ParsedLine::Line(txt_line) = mblock {
1026 let block = txt_line[block_index.1].clone();
1027 if let TxtLineNode::Tag(mut tag) = block {
1028 tag.set_attr("name", Some(name));
1029 txt_line[block_index.1] = TxtLineNode::Tag(tag);
1030 } else {
1031 return Err(anyhow::anyhow!(
1032 "Last tag block is not a tag: {:?}",
1033 mblock
1034 ));
1035 }
1036 } else {
1037 return Err(anyhow::anyhow!(
1038 "Last tag block is not a line: {:?}",
1039 mblock
1040 ));
1041 }
1042 }
1043 let mut message = m.message.clone();
1044 if let Some(repl) = replacement {
1045 for (k, v) in &repl.map {
1046 message = message.replace(k, v);
1047 }
1048 }
1049 let mut nodes = XMLTextParser::new(&message, lan).parse()?;
1050 if lang_block_index.is_some() && lang_end_block_index.is_some() {
1051 let start_index = lang_block_index.unwrap();
1052 let end_index = lang_end_block_index.unwrap();
1053 if start_index.1 != 0 {
1054 let block = output[start_index.0].clone();
1055 if let ParsedLine::Line(txt_line) = block {
1056 for i in 0..start_index.1 {
1057 nodes[0].insert(i, txt_line[i].clone());
1058 }
1059 } else {
1060 return Err(anyhow::anyhow!(
1061 "Lang block start is not a line: {:?}",
1062 block
1063 ));
1064 }
1065 }
1066 if end_index.1 + 1 < output[end_index.0].len() {
1067 let block = output[end_index.0].clone();
1068 if let ParsedLine::Line(txt_line) = block {
1069 for i in end_index.1 + 1..txt_line.len() {
1070 nodes.last_mut().unwrap().push(txt_line[i].clone());
1071 }
1072 } else {
1073 return Err(anyhow::anyhow!(
1074 "Lang block end is not a line: {:?}",
1075 block
1076 ));
1077 }
1078 }
1079 let ori_len = (end_index.0 - start_index.0 + 1) as isize;
1080 let new_len = nodes.len() as isize;
1081 for _ in start_index.0..=end_index.0 {
1082 output.remove(start_index.0);
1083 }
1084 let mut start_index = start_index.0;
1085 for node in nodes {
1086 output.insert(start_index, node);
1087 start_index += 1;
1088 }
1089 current_line = (current_line as isize + new_len - ori_len) as usize;
1090 } else {
1091 for node in nodes {
1093 output.insert(current_line, node);
1094 current_line += 1;
1095 }
1096 }
1097 lang_block_index = None;
1098 lang_end_block_index = None;
1099 mess = mes.next();
1100 last_tag_block_loc = None;
1101 } else if node.is_tag("selectbtn_init") {
1102 is_selectblk = true;
1103 } else if node.is_tag("selectbtn") {
1104 let lan = match lang.as_ref() {
1105 Some(l) => l.as_str(),
1106 None => {
1107 for key in node.tag_attr_keys() {
1108 if key == "label" || key == "call" {
1109 continue;
1110 }
1111 lang = Some(key.to_string());
1112 break;
1113 }
1114 match lang.as_ref() {
1115 Some(l) => l.as_str(),
1116 None => {
1117 return Err(anyhow::anyhow!(
1118 "No language found in selectbtn tag"
1119 ));
1120 }
1121 }
1122 }
1123 };
1124 let m = match mess {
1125 Some(m) => m,
1126 None => {
1127 return Err(anyhow::anyhow!("Not enough messages."));
1128 }
1129 };
1130 let mut message = m.message.clone();
1131 if let Some(repl) = replacement {
1132 for (k, v) in &repl.map {
1133 message = message.replace(k, v);
1134 }
1135 }
1136 let mut node = node.clone();
1137 node.tag_set_attr(lan, Some(message));
1138 let block = &mut output[current_line];
1139 if let ParsedLine::Line(txt_line) = block {
1140 txt_line[i] = node;
1141 } else {
1142 return Err(anyhow::anyhow!("selectbtn tag not in line: {:?}", block));
1143 }
1144 mess = mes.next();
1145 } else if node.is_tag("/selectbtn") {
1146 is_selectblk = false;
1147 } else if in_lang_block {
1148 } else if droped_lang_block {
1150 } else if is_selectblk {
1152 } else if let TxtLineNode::Tag(tag) = node {
1154 if !tag.is_blocked_name(&self.blacklist_names) {
1155 last_tag_block_loc = Some((current_line, i));
1156 }
1157 }
1158 }
1159 }
1160 current_line += 1;
1161 }
1162 let s = output.serialize();
1163 let encoded = encode_string(encoding, &s, false)?;
1164 file.write_all(&encoded)?;
1165 file.flush()?;
1166 Ok(())
1167 }
1168}
1169
1170pub fn read_tags_from_ini<P: AsRef<std::path::Path>>(path: P) -> Result<HashSet<String>> {
1172 let conf = ini::Ini::load_from_file(path)?;
1173 let set = HashSet::from_iter(conf.sections().flat_map(|s| s.map(|s| s.to_string())));
1174 eprintln!(
1175 "Read tags from ini: {}",
1176 set.iter().map(|s| s.as_str()).collect::<Vec<_>>().join(",")
1177 );
1178 Ok(set)
1179}
1180
1181#[test]
1182fn test_xml_parser() {
1183 let data = "测试文本\nok<r a=\"b\">测试<b o=\"文本\n换行\">";
1184 let data = XMLTextParser::new(data, "en").parse().unwrap();
1185 assert_eq!(
1186 data,
1187 vec![
1188 ParsedLine::Line(TxtLine(vec![
1189 TxtLineNode::Tag(TagNode {
1190 name: "lang".to_string(),
1191 attributes: vec![("en".to_string(), None)],
1192 }),
1193 TxtLineNode::Text(TextNode("测试文本".to_string())),
1194 TxtLineNode::Tag(TagNode {
1195 name: "rt2".to_string(),
1196 attributes: vec![],
1197 }),
1198 ])),
1199 ParsedLine::Line(TxtLine(vec![
1200 TxtLineNode::Text(TextNode("ok".to_string())),
1201 TxtLineNode::Tag(TagNode {
1202 name: "r".to_string(),
1203 attributes: vec![("a".to_string(), Some("b".to_string()))],
1204 }),
1205 TxtLineNode::Text(TextNode("测试".to_string())),
1206 TxtLineNode::Tag(TagNode {
1207 name: "b".to_string(),
1208 attributes: vec![("o".to_string(), Some("文本\n换行".to_string()))],
1209 }),
1210 TxtLineNode::Tag(TagNode {
1211 name: "/lang".to_string(),
1212 attributes: vec![],
1213 }),
1214 ])),
1215 ],
1216 );
1217}